/*
 * Copyright (c) 2013-2015 Charkey. All rights reserved.
 *
 * This software is the confidential and proprietary information of Charkey.
 * You shall not disclose such Confidential Information and shall use it only
 * in accordance with the terms of the agreements you entered into with Charkey.
 *
 * Charkey MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF THE SOFTWARE,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
 *
 * Charkey SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING,
 * MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES.
 */

package cn.simastudio.talos.core.service.base.impl;

import cn.simastudio.talos.common.constants.SimaConstants;
import cn.simastudio.talos.core.exception.biz.ConstraintCheckException;
import cn.simastudio.talos.core.exception.biz.DataDeleteException;
import cn.simastudio.talos.core.exception.biz.DataInsertException;
import cn.simastudio.talos.core.exception.biz.DataUpdateException;
import cn.simastudio.talos.core.mapper.base.OrganizationMapper;
import cn.simastudio.talos.core.mapper.base.UserOrgRelMapper;
import cn.simastudio.talos.core.model.base.Organization;
import cn.simastudio.talos.core.model.base.UserOrgRel;
import cn.simastudio.talos.core.service.base.OrganizationService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.BatchPreparedStatementSetter;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;

import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.Collections;
import java.util.List;

/**
 * Usage: OrganizationServiceImpl
 *
 * @author Charkey
 * @date 2015/7/8 19:21
 */
@Service
@SuppressWarnings("SpringJavaAutowiringInspection")
public class OrganizationServiceImpl implements OrganizationService {

    private static final Logger LOGGER = LoggerFactory.getLogger(OrganizationServiceImpl.class);

    @Autowired
    private OrganizationMapper organizationMapper;

    @Autowired
    private UserOrgRelMapper userOrgRelMapper;

    @Autowired
    private JdbcTemplate baseJdbcTemplate;

    @Override
    public Organization createOrganization(Organization organization) {
        int insertRes = organizationMapper.insert(organization);
        if (insertRes != SimaConstants.DEFAULT_UPDATE_COUNT) {
            throw new DataInsertException();
        }
        return organization;
    }

    @Override
    public boolean updateOrganization(Organization organization) {
        int updateRes = organizationMapper.updateByPrimaryKey(organization);
        if (updateRes != SimaConstants.DEFAULT_UPDATE_COUNT) {
            throw new DataUpdateException();
        }
        return true;
    }

    @Override
    public Organization getOrganization(Integer orgId) {
        return organizationMapper.selectByPrimaryKey(orgId);
    }

    @Override
    public boolean deleteOrganization(Integer orgId) {
        List<UserOrgRel> userOrgRels = userOrgRelMapper.getUserOrgRelListByOrgId(orgId);
        if (userOrgRels.size() > 0) {
            throw new ConstraintCheckException();
        }
        int deleteRes = organizationMapper.deleteByPrimaryKey(orgId);
        if (deleteRes != SimaConstants.DEFAULT_DELETE_COUNT) {
            throw new DataDeleteException();
        }
        return true;
    }

    @Override
    public List<Organization> getAllOrganizations() {
        return organizationMapper.getAllOrganizations();
    }

    @Override
    public List<Organization> getAllOrganizationsWithExclusion(Integer exclusiveOrgId) {
        Organization exclusiveOrg = organizationMapper.selectByPrimaryKey(exclusiveOrgId);
        if (exclusiveOrg == null) {
            return Collections.emptyList();
        }
        List<Organization> organizationList = organizationMapper.getAllOrganizationsWithExclusion(exclusiveOrg.getId(), exclusiveOrg.makeSelfAsNewParentIds());
        return organizationList != null ? organizationList : Collections.emptyList();
    }

    @Override
    public List<Organization> getSelfAndChildren(Integer rootOrgId) {
        Organization rootOrg = organizationMapper.selectByPrimaryKey(rootOrgId);
        if (rootOrg == null) {
            return Collections.emptyList();
        }
        List<Organization> organizationList = organizationMapper.getSelfAndChildren(rootOrg.getId(), rootOrg.makeSelfAsNewParentIds());
        return organizationList != null ? organizationList : Collections.emptyList();
    }

    @Override
    public boolean move(Integer sourceId, Integer targetId) {
        List<Organization> organizations = organizationMapper.selectMultiByKey(new Integer[]{sourceId, targetId});
        Organization sourceOrg = organizations.stream().filter(n -> n.getId().equals(sourceId)).findFirst().get();
        Organization targetOrg = organizations.stream().filter(n -> n.getId().equals(targetId)).findFirst().get();
        if (sourceOrg == null || targetOrg == null) {
            return false;
        }

        List<Organization> selfAndChildrenOfSource = organizationMapper.getSelfAndChildren(sourceOrg.getId(), sourceOrg.makeSelfAsNewParentIds());
        for (Organization organization : selfAndChildrenOfSource) {
            LOGGER.debug("BEFORE: {}", organization.toString());
        }
        String oldSourceParentIds = sourceOrg.getParentIds();

        selfAndChildrenOfSource.stream().forEach((n) -> n.setParentIds(n.getParentIds().replace(oldSourceParentIds, targetOrg.makeSelfAsNewParentIds())));
        for (Organization organization : selfAndChildrenOfSource) {
            LOGGER.debug("AFTER: {}", organization.toString());
        }
        sourceOrg.setParentId(targetOrg.getId());
        sourceOrg.setParentIds(targetOrg.makeSelfAsNewParentIds());
        int sourceUpdateRes = organizationMapper.updateByPrimaryKey(sourceOrg);

        int[] updateCounts = batchUpdate(selfAndChildrenOfSource);

        return true;
    }

    private int[] batchUpdate(List<Organization> organizations) {
        return baseJdbcTemplate.batchUpdate("UPDATE sima_organization org SET org.parent_ids = ? WHERE org.id = ?",
                new BatchPreparedStatementSetter() {
                    @Override
                    public void setValues(PreparedStatement ps, int i) throws SQLException {
                        ps.setString(1, organizations.get(i).getParentIds());
                        ps.setString(2, organizations.get(i).getId() + "");
                    }

                    @Override
                    public int getBatchSize() {
                        return organizations.size();
                    }
                });
    }
}
